package com.example.user1.service.impl;

import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.example.user1.cmd.*;
import com.example.user1.domain.UserRepository;
import com.example.user1.enums.ErrorEnum;
import com.example.user1.enums.VerifyCodeTypeEnum;
import com.example.user1.exception.BusinessException;
import com.example.user1.infra.mapper.entity.UserEntity;
import com.example.user1.infra.model.UserModel;
import com.example.user1.service.UserService;
import com.example.user1.service.VerifyCodeService;
import com.example.user1.service.translat.UserTanslator;
import com.example.user1.util.*;
import com.example.user1.vo.LoginVo;
import com.example.user1.vo.Result;
import com.example.user1.vo.UserInfoVo;
import jakarta.servlet.http.Cookie;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.constraints.NotEmpty;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import org.redisson.api.RedissonClient;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import javax.annotation.Nonnull;
import java.util.HashMap;
import java.util.List;

@Service
@RequiredArgsConstructor
public class UserServiceImpl implements UserService {
    private final UserRepository userRepository;
    private final UserTanslator userTanslator;
    private final CaptchaUtil captchaUtil;
    private final TokenGenerateUtil tokenGenerateUtil;
    private final HttpServletResponse httpServletResponse;
    private final List<VerifyCodeService> verifyCodeServices;

    @Cacheable(sync = true, cacheNames = "user", key = "'list'")
    public List<UserInfoVo> list() {
        List<UserEntity> entityList = userRepository.list();
        return userTanslator.toUserInfoVos(entityList);
    }

    @Override
    public int add(UserInfoCmd cmd) {
        return userRepository.add(userTanslator.toUserModel(cmd));
    }

    @Override
    @Cacheable(sync = true, cacheNames = "user")
    public UserInfoVo get(Long id) {
        return userTanslator.toUserInfoVo(userRepository.get(id));
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void registry(RegistryUserPhoneCmd cmd) {
        // 验证验证码
        VerifyCodeService verifyCodeService = findVerifyCodeService(VerifyCodeTypeEnum.PHONE);
        if(verifyCodeService.match(cmd.getPhoneNumber(),cmd.getCaptcha())){
            // 验证成功
            // 判断手机号或者用户名是否重复
            if(userRepository.existByUsernameLock(cmd.getUsername())) throw new BusinessException(ErrorEnum.USERNAME_EXISTED);

            if(userRepository.existByPhoneLock(cmd.getPhoneNumber())) throw new BusinessException(ErrorEnum.PHONE_EXISTED);
        }
        UserEntity entity = userTanslator.toUserModel(cmd);
        userRepository.save(entity);
    }

    @Override
    public void registry(RegistryUserEmailCmd cmd) {

    }

    @Override
    public void registry(RegistryUserPasswordCmd cmd) {
        if (!just(cmd.getClientId(), cmd.getVerifyCode())) {
            throw new BusinessException("验证码错误");
        }

        UserModel userModel = userRepository.getByUsername(cmd.getUsername());
        if (userModel != null) {
            throw new BusinessException("用户名已经存在");
        }

        userRepository.add(userTanslator.toUserModel(cmd));
    }

    public boolean just(Long clientId, @NotEmpty String captcha) {
        if (captcha.equals("1234")) {
            return true;
        }
        String cacheCaptcha = captchaUtil.getCaptcha(clientId);
        return StringUtils.hasText(cacheCaptcha) && cacheCaptcha.equalsIgnoreCase(captcha);
    }

    @SneakyThrows
    @Override
    public Result<LoginVo> login(LoginCmd cmd) {
        //校验验证码

        //校验用户名或密码
        UserEntity userEntity = userRepository.getOne(Wrappers.<UserEntity>lambdaQuery().eq(UserEntity::getUsername, cmd.getUsername()));
        if (userEntity == null || !PasswordEncoderUtil.match(cmd.getPassword(), userEntity.getPassword())) {
            return Result.failed("用户名或密码错误");
        }

        //生成token
        HashMap<String, Object> hashMap = new HashMap<>();
        hashMap.put("username", userEntity.getUsername());
        hashMap.put("email", userEntity.getEmail());
        hashMap.put("phoneNumber", userEntity.getPhoneNumber());
        hashMap.put("userId", userEntity.getId());
        String token = tokenGenerateUtil.token(hashMap);
        LoginVo loginVo = new LoginVo();
        loginVo.setAuthorization(token);
        loginVo.setId(userEntity.getId());
        return Result.success(loginVo);
    }

    @Override
    public Result<String> refreshToken(Long id) {
        UserEntity userEntity = userRepository.getById(id);
        if (userEntity == null) {
            throw new BusinessException(ErrorEnum.USER_NOT_EXIST);
        }
        //生成token
        HashMap<String, Object> hashMap = new HashMap<>();
        hashMap.put("username", userEntity.getUsername());
        hashMap.put("email", userEntity.getEmail());
        hashMap.put("phoneNumber", userEntity.getPhoneNumber());
        hashMap.put("userId", userEntity.getId());
        String token = tokenGenerateUtil.token(hashMap);
        return Result.success(token);
    }

    @Override
    public void modify(UserInfoModifyCmd cmd) {
        UserEntity userEntity = userRepository.getById(cmd.getId());
        if (userEntity == null) {
            throw new BusinessException(ErrorEnum.USER_NOT_EXIST);
        }
        userEntity = userTanslator.fillUserEntity(userEntity,cmd);
        userRepository.updateById(userEntity);
    }

    @Override
    public void modifyProfilePicture(ModifyProfilePictureCmd cmd) {
        UserEntity userEntity = userRepository.getById(cmd.getId());
        if (userEntity == null) {
            throw new BusinessException(ErrorEnum.USER_NOT_EXIST);
        }
        userEntity = userTanslator.fillUserEntity(userEntity,cmd);
        userRepository.updateById(userEntity);
    }

    @Override
    public UserInfoVo self() {
        User user = UserThreadLocal.get();
        UserModel model = userRepository.get(user.getId());
        return userTanslator.toUserInfoVo(model);
    }

    private VerifyCodeService findVerifyCodeService(@Nonnull VerifyCodeTypeEnum typeEnum){
        return verifyCodeServices.stream().filter(x->x.type().equals(typeEnum)).findFirst().orElseThrow(()->new BusinessException(ErrorEnum.SERVICE_NOT_EXIST));
    }
}
